/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package library.JpegNodes;

import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.util.Hashtable;

import org.apache.log4j.*;
import org.cmc.sanselan.ImageReadException;

import library.FileFilters.*;
import library.Configuration.*;
import library.RemoteControl.RemoteControlInterface;

/**
 * This class implements the behavior of a directory that contains Jpeg files
 * whatever the action the user requested.
 * @author JNC
 */

@SuppressWarnings("serial")
public class JpegDirectory extends File {
    
    /**
     * The pattern to count the max index
     */
    private static final String _FILENAME_PATTERN_INDEX = Configuration.getStringProperty("JpegDirectory.FilenamePatternIndex");
    
    /**
     * The increment in the filename when multiple files share the same date and title
     */
    private static final int _INDEX_INCREMENT = Configuration.getIntProperty("JpegDirectory.IndexIncrement") ;;
    
    /**
     * The initial index in the file name when renaming files
     */
    private static final int _INDEX_INIT = Configuration.getIntProperty("JpegDirectory.IndexInit"); ;
    
    /**
     * The log4j log
     */
    private static final Logger _logger = Logger.getLogger(JpegDirectory.class);

    /**
     * The remote control to interact with the frame.
     */
    private RemoteControlInterface _remoteControl;
    
    /**
     * Indicator that will cause the processing to stop. <br/>
     * Is stored as an attribute as the thread will stop asynchronously.
     */
    private static boolean _requestToStop;

    /**
     * The total number of files to process (can be complex to get when 
     * processing starting directory recursively)
     */
    private static int _nbrFilesToProcess;
    
    /**
     * The number of files processed
     */
    private static int _nbrFilesProcessed;
    
    /**
     * Hash table to speed the incremented index processings
     */
    private Hashtable<String, Integer> _indexHashTable;
        
    /**
     * Constructor
     * @param mysDirectoryPath
     * The starting directory path to apply the action
     * @param myAction
     * An indicator recalling the action the user requested
     * @param mybRecursiveProcess
     * An indicator recalling if the user requested a recursive processing
     * @param myExchangeZone
     * Reference to the remote control that enables interactions with the frame
     */
    public JpegDirectory(String mysDirectoryPath,
            RemoteControlInterface myRemoteControl) {
        
        super(mysDirectoryPath);

        _remoteControl = myRemoteControl;
        
        _requestToStop = false;
        _indexHashTable = new Hashtable<String, Integer>();
        
        // do not initialize static _nbrFilesToProcess and _nbrFilesProcessed in the constructor
        // or those variables will be initialized erratically whenver a Class_JpegDirectory object is created
    }
        
    /**
     * A unique method that handles all possible actions requested by the user
     * The rationale is always the same :
     * <ul>
     *  <li>get all files from the directory (potentially recursively process the sub directories)
     *  <li>apply the requested action to all files according to what the user requested (check box)
     * </ul>
     * If the stop is requested, the key point is to avoid the break the process
     * in an unconsistent manner
     */
    public void processJpegFiles(ActionTypeEnum myAction, 
            boolean myIsRecursiveProcess) {
        
        boolean bAbort = false;
        File[] lChildren = null;
        JpegFileFilter xFilter = null;
        
        // test if the processing is requested to stop
        if (_requestToStop) {
            _logger.debug("Request to stop received : nothing will be processed");
            bAbort = true;
        }
    
        // create the relevant filter
        // --------------------------
        if (!bAbort) {
            xFilter = fileFilter(myAction, myIsRecursiveProcess);
            if (xFilter == null) {
                _logger.debug("fileFilter returned null");
                bAbort = true;
            }
        }
        
        // get all files from the directory
        // --------------------------------
        if (!bAbort) {
            // show a progress bar in indeterminate mode
            _remoteControl.startIndeterminateProgressBar();
            
            lChildren = listFiles(xFilter);
            
            // stop the progress bar in indeterminate mode
            _remoteControl.stopIndeterminateProgressBar();

            if (lChildren == null) {
                // either this is not a directory or does not exist
                _logger.error("The file " + getAbsolutePath() + " does not exist or is a directory");
                bAbort = true;
            } // if lChildren == null
        } // if ! bAbort
        
        // process all files from the directory
        // ------------------------------------
        if (! bAbort) {
            _logger.info(lChildren.length + " files to process in directory " + getName());
            _nbrFilesToProcess = _nbrFilesToProcess + lChildren.length;
            
            // start the determinate progress bar
            _remoteControl.initDeterminateProgressBar();
            
            // loop all files
            // --------------
            for (int iLoop = 0; ((iLoop < lChildren.length) && (!_requestToStop)); iLoop++) {
                // the loop is interrupted if the stop is requested
                
                // indicate the progress
                _logger.debug("Processing " + lChildren[iLoop].getName());
                _nbrFilesProcessed ++;
                _remoteControl.setDeterminateProgressBar((int)(100*_nbrFilesProcessed/_nbrFilesToProcess));

                // process the file or the directory
                // ---------------------------------
                File xFile = lChildren[iLoop];
                
                if (xFile.isFile()) { 
                    
                    processAFile(xFile, myAction);
                } else { // if xFile.isFile
                    // this is a directory
                    // -------------------
                    // not necessary to test the _recursiveProcess boolean to process the directory
                    // the filter has been parameterized to include directories
                    // only if the _recursiveProcess is enabled
                    
                    // recursively process the directory
                    // ---------------------------------
                    new JpegDirectory(xFile.getAbsolutePath(), _remoteControl)
                            .processJpegFiles(myAction, myIsRecursiveProcess);
                } // if ! xFile.isFile
            } // for
            
            // logs the event when a request to stop has been received
            if (_requestToStop) {
                _logger.debug("Request to stop received : loop broken while processing the files");
            } else {
                _logger.debug("The loop ended as all files are processed");
            }
            
        } // if ! bAbort      
    }      

    /**
     * create the appropriate file filter depending on the action requested by the user
     * @return
     * returns the appropriates file filter objet (Class_RenameFileFilter or Class_CheckFileFilter)
     */
    private JpegFileFilter fileFilter(ActionTypeEnum myAction, boolean myIsRecursiveProcess) {
        switch(myAction) {
            case Rename :
                return new RenameFileFilter(myIsRecursiveProcess);
            case Check :
                return new CheckFileFilter(myIsRecursiveProcess);
            default :
                _logger.error("Unknown action type = " + myAction);
                return null;
        } // switch
    }
    
    private void processAFile (File myFile, ActionTypeEnum myAction) {
        
        int incrementedIndex ;
        
        incrementedIndex = 0;
        
        // process the file accordingly to the user-requested action
        // ---------------------------------------------------------
        try {
            switch(myAction) {
                case Rename :
                    // create a new Class_JpegFile
                    JpegFile jpegFile = new JpegFile(myFile);
                    
                    // gets the incremented index corresponding to the digitalization date of this file
                    incrementedIndex = getIncrementedIndex(jpegFile.getDigitalizationDateString());
                    
                    // rename the jpegfile and returns the new filename
                    jpegFile.rename(incrementedIndex);
                    
                    // show the picture only if the user requested so (the checkbox is tested in real time)
                    // sets the labels in the mainframe only if the user requested so (the checkbox is tested in real time)
                    _remoteControl.setLabelsAndShowPicture(
                                        jpegFile.getName(),
                                        jpegFile.getDigitalizationDateString(),
                                        jpegFile.getTitle(),
                                        jpegFile.shortNewName(),
                                        jpegFile.longNewName());
                    break;
                    
                case Check :
                    new JpegFile(myFile).check();
                    break;
                    
                default :
                    _logger.error("Found invalid code " + myAction);
                    break;
            } // switch                                       
        } // try
        catch (ImageReadException exceptionImageRead) {
            _logger.error("ImageReadException caught while processing " + myFile.getName() + " : " + exceptionImageRead);
        }
        catch (IOException exceptionIO) {
            _logger.error("IOException caught while processing " + myFile.getName() + " : " + exceptionIO);
        }
        catch (ParseException exceptionParse) {
            _logger.error("ParseException caught while processing " + myFile.getName() + " : " + exceptionParse);            
        }
        catch(NullPointerException exceptionNullPointer) {
            _logger.error("NullPointerException caught while processing " + myFile.getName() + " : " + exceptionNullPointer);
        }
    }
     
    /**
     * Initialize the static nbr of files to process and the nbr of files already processed
     * Those static attributes can not be initialized in the constructor
     * otherwise they will be initialized erratically whenever a Class_JpegDirectory object is created
     */
    public void resetNbrFiles() {
        _nbrFilesToProcess = 0;
        _nbrFilesProcessed = 0;
    }
    
    /**
     * gets the incremented index for the given digitalization date and for this directory
     * @param myDigitalizationDate
     * @return
     */
    private int getIncrementedIndex(String mysDigitalizationDate) {
        String sPattern;
        File[] lFiles;
        String[] lWords;
        Integer returnedIndex;
        int incrementedIndex ;
        int iIndex;
        int iMaxIndex;  
              
        iMaxIndex = -1;
        
        // check if the digitalization date can be found in the hash table, and returns the max existing index
        returnedIndex = (Integer)_indexHashTable.get(mysDigitalizationDate);

        // if nothing found in the hash table
        if (returnedIndex == null) {
            
            // must check all files of the directory
            sPattern = String.format(_FILENAME_PATTERN_INDEX, mysDigitalizationDate);  
            
            _logger.debug("Pattern = " + sPattern);
            IndexFileFilter xFilter = new IndexFileFilter(sPattern);
        
            lFiles = listFiles(xFilter);
            if (lFiles == null) {
                _logger.error("listFiles returned null");
            } else {
                // retrieve the highest index
                for (File xFile : lFiles) {
                    lWords = xFile.getName().split("\\s");
                    iIndex = new Integer (lWords[3]);
                    
                    if (iMaxIndex < iIndex) {
                        iMaxIndex = iIndex;
                    } //if
                } // for
            } // else
        } else { // if
            
            // if the hash table had a value
            iMaxIndex = returnedIndex;
        }
        
        // calculate the incremented index from the max index
        if (iMaxIndex != -1) {
            incrementedIndex = iMaxIndex + _INDEX_INCREMENT;
        } else {
            incrementedIndex =  _INDEX_INIT ;
        }
        
        // update the hash table for the next time
        _indexHashTable.put(mysDigitalizationDate, (Integer)incrementedIndex);
        
        // returns value
        return incrementedIndex;
    }
    
    /**
     * requets the processing to stop
     */
    public void requestToStop() {
        _logger.debug("Request to stop");
        _requestToStop = true;
    }
}